package Project;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

import javax.swing.JTextArea;

import org.jfree.data.gantt.TaskSeries;

public class Myopic {
	private final static String newline = "\n";
	private JTextArea textArea;
	private int maxBacktracks;
	private int feasabilityCheckWindow;
	private int WVAL;
	private Processor processorOne;
	private Processor processorTwo;
	private ArrayList<SingleTask> taskSet = new ArrayList<SingleTask>();
	public Myopic( int processors, int backtracks, int kValue, int WValue, ArrayList<SingleTask> tasks, JTextArea area)
	{
		maxBacktracks = backtracks;
		feasabilityCheckWindow = kValue;
		taskSet = tasks;
		WVAL = WValue;
		processorOne = new Processor(1);
		processorTwo = new Processor(2);
		textArea = area;
	}
	public void populateProcessorOneTaskSeries(TaskSeries t)
	{
		processorOne.populateTaskSeries(t);
	}
	public void populateProcessorTwoTaskSeries(TaskSeries t)
	{
		processorTwo.populateTaskSeries(t);
	}
	private int Schedule(ArrayList<tuple> HeuristicSet)
	{
		int procNumber;
		SingleTask tasktoSchedule =  HeuristicSet.get(0).task;
		int lowestEST = HeuristicSet.get(0).EST;

		int One = lowestEST - processorOne.getEarliestAvailableTime();
		int Two = lowestEST - processorTwo.getEarliestAvailableTime();
		
		if( Two < 0 || One >= Two )
		
		//if(Math.abs(One) <= Math.abs(Two) )
		{
			textArea.append("Scheduling Task T" + tasktoSchedule.getTaskId() + " on Processor One from " + lowestEST + " to " + ( lowestEST + tasktoSchedule.getCompTime() ) + newline );
			processorOne.Schedule(lowestEST, lowestEST + tasktoSchedule.getCompTime(), tasktoSchedule);
			procNumber = processorOne.getProcessorID();
		}
		else
		{
			textArea.append("Scheduling Task T" + tasktoSchedule.getTaskId() + " on Processor Two from " + lowestEST + " to " + (lowestEST + tasktoSchedule.getCompTime()) + newline);
			processorTwo.Schedule(lowestEST, lowestEST + tasktoSchedule.getCompTime(), tasktoSchedule);
			procNumber = processorTwo.getProcessorID();
		}
		taskSet.remove(tasktoSchedule);
		return procNumber;
	}
	public boolean ExecuteMyopic()
	{
		SortTasks();
		int numbacktracks = 0;
		ArrayList<Integer> lastProc = new ArrayList<Integer>();
		ArrayList<ArrayList<tuple>> lastHeuristicSet = new ArrayList<ArrayList<tuple>>();
		while (taskSet.size() > 0 )
		{
			ArrayList<tuple> HeuristicSet = new ArrayList<tuple>();
			boolean needsBacktrack = false;

			for(int i = 0; i< Math.min(taskSet.size(), feasabilityCheckWindow); i++)
			{		
				SingleTask t = taskSet.get(i);
				int EST = ESTValue(t);
				if( EST == -1 )
				{
					needsBacktrack = true;
					break;
				}	

				int heuristic = ( EST * WVAL ) + t.getDeadline();
				textArea.append("Heuristic value of T" + t.getTaskId() + " :" + heuristic + newline);
				HeuristicSet.add(new tuple(heuristic, t, EST ));
			}
			HeuristicSet = SortHVals(HeuristicSet);
			if(!needsBacktrack)
			{
				lastProc.add( Schedule(HeuristicSet) );
				lastHeuristicSet.add(HeuristicSet);
			}
			else
			{
				if(numbacktracks < maxBacktracks )
				{  
					textArea.append("BackTrack Need Detected" + newline);
					numbacktracks++;
					int num = -1;
					if(lastProc.size() > 0){
						num = lastProc.remove(lastProc.size()-1);
					}
					else
						return false;
					
					if( num == 1)
					{
						processorOne.Backtrack( textArea );
					}
					else if(num == 2)
					{
						processorTwo.Backtrack(textArea);
					}
					
					
					ArrayList<tuple> hset = lastHeuristicSet.get(lastHeuristicSet.size()-1);
					//TODO need to backtrack twice
					if( hset.size() == 1 )
					{
						taskSet.add((hset.remove(0).task));
						lastHeuristicSet.remove(hset);
						
						numbacktracks++;
						if(lastProc.remove(lastProc.size()-1) == 1)
						{
							processorOne.Backtrack( textArea );
						}
						else if(lastProc.remove(lastProc.size()-1) == 2)
						{
							processorTwo.Backtrack(textArea);
						}
						
						ArrayList<tuple> hset2 = lastHeuristicSet.get(lastHeuristicSet.size()-1);
						taskSet.add((hset2.remove(0).task));
						SortTasks();
						lastProc.add(Schedule(hset2));
					}
					else if( hset.size() > 1 )
					{
						taskSet.add((hset.remove(0).task));
						SortTasks();
						lastProc.add(Schedule(hset));
					}
					

					
					//System.out.println("Scheduling Backtracked Task T" + toReschedule.getTaskId() + " on Processor P" + otherProcessor(lastProcessorScheduledOn).getProcessorID() + " from " + lowestEST + " to " + (lowestEST + tasktoSchedule.getCompTime()) );
					//processorTwo.Schedule(EST, EST + toReschedule.getCompTime(), toReschedule);
					//lastProcessorScheduledOn = otherProcessor(lastProcessorScheduledOn);

				}
				else
				{
					textArea.append("too many backtracks");
					return false;

				}
			}
		}

		return true;
	}
	private ArrayList<tuple> SortHVals(ArrayList<tuple> t)
	{
		Collections.sort(t, new Comparator<tuple>() {

			@Override
			public int compare(tuple a, tuple b) {
				if(a.H < b.H)
				{
					return -1;
				}
				else if (a.H > b.H)
				{
					return 1;
				}
				else
					return 0;
			}
		});
		return t;
	}
	private void SortTasks()
	{
		Collections.sort(taskSet, new Comparator<SingleTask>() {
			public int compare(SingleTask a, SingleTask b) {
				if(a.getDeadline() < b.getDeadline())
				{
					return -1;
				}
				else if (a.getDeadline() > b.getDeadline())
				{
					return 1;
				}
				else
					return 0;
			}
		});
	}
	private Processor otherProcessor(Processor p)
	{
		if( p == processorOne)
			return processorTwo;
		else 
			return processorOne;
	}
	public int getMaximumReady()
	{
		return Math.max(processorOne.getEarliestAvailableTime(), processorTwo.getEarliestAvailableTime());
	}
	private int ESTValue(SingleTask t)
	{
		//EST(Ti) = Max(ri, min(avail_time(j)), max(EATuk)))
		int Ri = t.getReadyTime();
		int avail_time = Math.min( processorOne.getEarliestAvailableTime(), processorTwo.getEarliestAvailableTime());
		int EAT1 = avail_time;
		int EAT2 = avail_time;
		Processor p;
		if( processorOne.getEarliestAvailableTime() < processorTwo.getEarliestAvailableTime() )
			p = processorOne;
		else 
			p = processorTwo;

		//EAT1
		int timetocheck = Math.max(Ri, p.getEarliestAvailableTime());
		Resource r = otherProcessor(p).getTaskAtTime( timetocheck ).getResource();
		Resource r2 = otherProcessor(p).getTaskAtTime( timetocheck + t.getCompTime()).getResource();
		if( t.getResource() == Resource.EXCLUSIVE ) 
		{
			if( r2 == Resource.EXCLUSIVE || r2 == Resource.SHARE || r == Resource.EXCLUSIVE || r == Resource.SHARE )
				EAT1 = otherProcessor(p).getEarliestAvailableTime();
		}
		else if( t.getResource() == Resource.SHARE ) 
		{
			//Resource r = otherProcessor(p).getTaskAtTime(p.getEarliestAvailableTime()).getResource();
			if( r == Resource.EXCLUSIVE || r2 == Resource.EXCLUSIVE )
				EAT1 = otherProcessor(p).getEarliestAvailableTime();
		}

		//EAT2

		Resource rec = otherProcessor(p).getTaskAtTime(timetocheck).getResourceTwo();
		Resource rec2 = otherProcessor(p).getTaskAtTime(timetocheck + t.getCompTime()).getResourceTwo();
		if( t.getResourceTwo() == Resource.EXCLUSIVE ) 
		{
			//Resource r = otherProcessor(p).getTaskAtTime(p.getEarliestAvailableTime()).getResourceTwo();
			if( rec == Resource.EXCLUSIVE || rec == Resource.SHARE || rec2 == Resource.EXCLUSIVE || rec2 == Resource.SHARE )
				EAT1 = otherProcessor(p).getEarliestAvailableTime();
		}
		else if( t.getResourceTwo() == Resource.SHARE ) 
		{
			//Resource r = otherProcessor(p).getTaskAtTime(p.getEarliestAvailableTime()).getResourceTwo();
			if( rec == Resource.EXCLUSIVE || rec2 == Resource.EXCLUSIVE ){
				EAT1 = otherProcessor(p).getEarliestAvailableTime();

			}
		}
		//textArea.append("Taking Max of: (" + Ri + ", " + avail_time + ", " + Math.max(EAT1, EAT2)+ ") for task T" + t.getTaskId() + newline );
		int EST = Math.max(Math.max(Ri, avail_time), Math.max(EAT1, EAT2));

		if( EST + t.getCompTime() <= t.getDeadline() )
			return EST;
		else
			return -1;
	}

	private class tuple{
		public int H;
		public int EST;
		public SingleTask task;
		public tuple( int h, SingleTask t, int est ) 
		{
			task = t;
			H = h;
			EST = est;
		}
	}
}
